
//---------------------------
// Terminal functions

//$ct_defaultColor = 50;
$ct_defaultColor = 32;
$ct_printGroup = Print_Terminal_NonoSansMono;
$ct_maxControllers = 4;

function ct_createTerminalBrick(%term, %brickgroup, %nx, %ny, %pos, %angle, %x, %y) {
	if     (%angle==0) { %brickX =  mFloor(%x/%nx)/2; %brickY = 0; %angleId = 3; }
	else if(%angle==1) { %brickY = -mFloor(%x/%nx)/2; %brickX = 0; %angleId = 0; }
	else if(%angle==2) { %brickX = -mFloor(%x/%nx)/2; %brickY = 0; %angleId = 1; }
	else if(%angle==3) { %brickY =  mFloor(%x/%nx)/2; %brickX = 0; %angleId = 2; }
	%brickZ = -mFloor(%y / %ny)/5;
	%brickPos = vectorAdd(%pos, %brickX SPC %brickY SPC %brickZ);
	%subX = %x % %nx; %subY = %y % %ny;
	//if(%nx==1 && %ny==0.25) {
	//	%db = "brickTerminalLetterData";
	//}else if(%nx==4 && %ny==1) {
	//	%db = "brickSmallTerminalLetter" @ %subX @ "Data";
	//} else {
		%db = "brickMicroTerminal" @ %nx @ "x" @ %ny @ "at" @ %subX @ "x" @ %subY @ "Data";
	//}
	
	%brick = new fxDtsBrick() {
		datablock = %db;
		angleId = %angleId;
		colorId = %color;
		isPlanted = 1;
	};
	if(!isObject(%brick)) { talk("ct_terminalCreateBrick: brick not created: " @ %x SPC %y); return ""; }
	
	%brickgroup.add(%brick);
	
	%brick.setTransform(%brickPos SPC "0 0 1" SPC (%angleId * $pi/2));
	%brick.trustCheckFinished();
	%err = %brick.plant();
	if(%err==1 || %err==3 || %err==5) { %brick.delete(); talk("ct_terminalCreateBrick: plant error " @ %err @ ": " @ %x SPC %y); return ""; }
	
	%brick.setRaycasting(0);
	%brick.setColliding(0);
	%brick.setColor($ct_defaultColor);
	%brick.setColorFx(3);
	%brick.setPrint($printNameTable[$ct_printGroup.a[32]]);
	
	%brick.isIgnoredByAutoSaver = 1;
	
	%term.ct_brick[%x, %y] = %brick;
	%brick.ct_parentTerm = %term;
}

// Brick group, PPB X,Y, Position of stud of top-left corner, dir N/E/S/W, chars horz,vert
// returns terminal object
function mMin(%a, %b) {
	return %a<%b ? %a : %b;
}
function ct_createBricks(%term, %start, %count) {
	%end = mMin(%start + %count, %term.ct_sx * %term.ct_sy);
	for(%i=%start; %i<%end; %i++) {
		%x = %i % %term.ct_sx;
		%y = mFloor(%i / %term.ct_sx);
		ct_createTerminalBrick(%term,
			%term.ct_brickgroup, %term.ct_nx, %term.ct_ny,
			%term.ct_pos, %term.ct_angle,
			%x, %y
		);
	}
}
function ct_create(%brickgroup, %nx, %ny, %pos, %angle, %sx, %sy) {
	clearCurrentQuotaObject();
	%term = new ScriptObject();
	%term.ct_brickgroup = %brickgroup;
	%term.ct_nx = %nx;
	%term.ct_ny = %ny;
	%term.ct_pos = %pos;
	%term.ct_angle = %angle;
	%term.ct_sx = %sx;
	%term.ct_sy = %sy;
	//%bps = 10;
	//%end = mCeil(%sx * %sy);
	//for(%i=0; %i<%end; %i+=%bps) {
	//	%s = schedule(%i/%bps*100, 0, ct_createBricks, %term, %i, %bps);
	//}
	ct_createBricks(%term, 0, %sx*%sy);
	ct_updateControllerList(%term);
	return %term;
}
function ct_delete(%term) {
	ct_clearControllers(%term, 1);
	ct_clearMainControl(%term);
	for(%y=0; %y<%term.ct_sy; %y++) {
		for(%x=0; %x<%term.ct_sx; %x++) {
			%brick = %term.ct_brick[%x, %y];
			if(isObject(%brick)) %brick.delete();
			%term.ct_brick[%x, %y] = "";
		}
	}
	%term.delete();
}
function ct_clear(%term) {
	for(%y=0; %y<%term.ct_sy; %y++) {
		for(%x=0; %x<%term.ct_sx; %x++) {
			%brick = %term.ct_brick[%x, %y];
			if(isObject(%brick)) {
				%brick.setColor($ct_defaultColor);
				%brick.setPrint($printNameTable[$ct_printGroup.a[32]]);
			} else {
				talk("ct_clear - brick does not exist at " @ %x @ " " @ %y);
			}
		}
	}
}

//---------------------------------

$ct_asciiChars = " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
function ct_charToAscii(%c) {
	return strPos($ct_asciiChars, %c) + 32;
}
function ct_setChar(%term, %x, %y, %c, %inv) {
	if(!(%x>=0 && %x<%term.ct_sx && %y>=0 && %y<%term.ct_sy)) return;
	%brick = %term.ct_brick[%x, %y];
	if(isObject(%brick)) {
		%brick.setPrint(
			$printNameTable[
				$ct_printGroup.a[
					ct_charToAscii(%c)
				] @
				(%inv ? "-Inv" : "")
			]
		);
	} else {
		%term.ct_brick[%x, %y] = "";
		//ct_createTerminalBrick(%term.ct_brickgroup, %term.ct_nx, %term.ct_ny, %term.ct_pos, %term.ct_angle, %x, %y);
		talk("ct_setChar - brick does not exist at " @ %x SPC %y);
	}
}
function ct_printChars(%term, %x, %y, %str, %inv) {
	%len = strLen(%str);
	for(%i=0; %i<%len; %i++) {
		%c = getSubStr(%str, %i, 1);
		ct_setChar(%term, %x, %y, %c, %inv);
		%x++;
		if(%x >= %term.ct_sx) { %x = 0; %y++; }
	}
}

//---------------------------------
// Brick functions

function fxDtsBrick::ct_create(%brick, %nx, %ny, %sx, %sy) {
	%brick.ct_delete();
	if(!(
		(%nx== 1 && %ny==0.25) ||
		(%nx== 4 && %ny==1   ) ||
		(%nx==16 && %ny==4   )
	)) { talk("fxDtsBrick::ct_create - scale is invalid: " @ %nx @ "x" @ %ny); return; }
	if(!(
		(%sx % %nx == 0) &&
		(%sy % %ny == 0)
	)) { talk("fxDtsBrick::ct_create - size is not divisible by scale"); return; }
	%dbname = %brick.getDatablock().getName();
	if(!(
		%dbname $= "brick05x1fPlateNoOverlapData" ||
		%dbname $= "brick05x1fPlateData_NoOverlap" ||
		%dbname $= "brick1x1fPrintData"
	)) { talk("fxDtsBrick::ct_create - root brick is invalid db"); return; }
	%angleOffset = (%dbName $= "brick05x1fPlateNoOverlapData") ? 2 : 0;
	
	%brickgroup = %brick.getGroup();
	%pos = vectorAdd(%brick.getPosition(), "0 0 -0.2");
	%angle = (%brick.angleId + %angleOffset) % 4;
	%term = ct_create(%brickgroup, %nx, %ny, %pos, %angle, %sx, %sy);
	if(!isObject(%term)) { talk("fxDtsBrick::ct_create - Failed to create terminal"); return; }
	%brick.ct_term = %term;
}
function fxDtsBrick::ct_delete(%brick) {
	%term = %brick.ct_term;
	if(isObject(%term)) ct_delete(%term);
	%brick.ct_term = "";
}
package ct_customTerminal {
	function fxDTSBrickData::onDeath(%this, %brick){
		if(%brick.ct_term) %brick.ct_delete();
		parent::onDeath(%this, %brick);
	}
	function fxDTSBrickData::onRemove(%this, %brick) {
		if(%brick.ct_term) %brick.ct_delete();
		parent::onRemove(%this, %brick);
	}
};

//---------------------------------
// Client interface functions

function ct_deleteClient(%client) {
	ct_clearClientController(%client);
	for(%i=0; %i<%client.ct_numControlNames; %i++) {
		%name = %client.ct_controlName[%i];
		ct_clientClearMainControl(%client, name);
		%client.ct_controlName[%i] = "";
	}
	%client.ct_numControlNames = "";
}
function ct_updateControllerList(%term) {
	%x = %term.ct_sx-16;
	%y = %term.ct_sy-$ct_maxControllers-1;
	// todo - display names somewhere
	for(%idx=0; %idx<$ct_maxControllers; %idx++) {
		%client = %term.ct_controller[%idx];
		if(isObject(%client)) {
			%name = %client.name;
		} else {
			%name = "";
			if(%client !$= "") {
				talk("ct_updateControllerList - client no longer exists: " @ %client);
				%term.ct_controller[%idx] = "";
			}
		}
		ct_printChars(%term, %x, %y+%idx, "                ", 1);
		ct_printChars(%term, %x, %y+%idx, %name, 1);
	}
	%idx = $ct_maxControllers;
	%client = %term.ct_mainController;
	if(isObject(%client)) {
		%name = %client.name;
	} else {
		%name = "";
		if(%client !$= "") {
			talk("ct_updateControllerList - main controller client no longer exists: " @ %client);
			%term.ct_mainController = "";
			%term.ct_mainControlName = "";
		}
	}
	ct_printChars(%term, %x, %y+%idx, "                ", 1);
	ct_printChars(%term, %x, %y+%idx, %name, 1);
}
function ct_setController(%term, %idx, %client, %mouse) {
	if(!isObject(%term)) { talk("ct_setController - terminal does not exist"); return; }
	if(!isObject(%client)) { talk("ct_setController - client does not exist"); return; }
	if(!(%idx>=0 && %idx<$ct_maxControllers)) { talk("ct_setController - Invalid idx " @ %idx); return; }
	
	if(%term == %client.ct_term && %idx == %client.ct_controllerIdx && %mouse == %client.ct_useMouse) return;
	
	ct_clearClientController(%client, %client.ct_term==%term);
	ct_clearController(%term, %idx, 1);
	
	%term.ct_controller[%idx] = %client;
	%term.ct_useMouse[%idx] = %mouse;
	%client.ct_term = %term;
	%client.ct_controllerIdx = %idx;
	%client.ct_useMouse = %mouse;
	
	ct_clientControlStart(%term, %client);
	
	ct_updateControllerList(%term);
}
function ct_clearController(%term, %idx, %noUpdate) {
	if(%term.ct_controller[%idx] !$= "") {
		%client = %term.ct_controller[%idx];
		if(isObject(%client)) {
			%client.ct_term = "";
			%client.ct_controllerIdx = "";
			%client.ct_useMouse = "";
			ct_clientControlStop(%term, %client);
		}
		%term.ct_controller[%idx] = "";
		%term.ct_useMouse[%idx] = "";
	}
	if(!%noUpdate) ct_updateControllerList(%term);
}
function ct_clearControllers(%term, %noUpdate) {
	for(%idx=0; %idx<$ct_maxControllers; %idx++) {
		ct_clearController(%term, %idx, 1);
	}
	if(!%noUpdate) ct_updateControllerList(%term);
}
function ct_clearClientController(%client, %noUpdate) {
	%term = %client.ct_term;
	if(isObject(%term)) {
		%idx = %client.ct_controllerIdx;
		ct_clearController(%term, %client.ct_controllerIdx, %noUpdate);
		ct_clientControlStop(%term, %client);
	}
	%client.ct_term = "";
}

function ct_clientControlStart(%term, %client) {
	if(!%client.ct_active) {
		%client.ct_active = 1;
		//talk("Start " @ %client.name);
		commandToClient(%client, 'LStartInput', %client.ct_useMouse);
	}
}
function ct_clientControlStop(%term, %client) {
	if(%client.ct_active) {
		%client.ct_active = 0;
		//talk("Stop " @ %client.name);
		commandToClient(%client, 'LStopInput');
	}
}

function ct_termInputKey(%term, %idx, %key, %state) {
	//talk(%term SPC %idx SPC %key SPC %state);
	%client = %term.ct_mainController;
	if(isObject(%client)) {
		if(%client.ct_term != %term) {
			talk("ct_termInputKey - client's term is not term");
			return;
		}
		%name = %term.ct_mainControlName;
		commandToClient(%client, 'ct_inputKey', %name, %idx, %key, %state);
	} else if(%client !$= "") {
		talk("ct_termInputKey - controller 0 client " @ %client @ " no longer exists");
		ct_clearController(%term, 0);
	}
}
function ct_clientInputKey(%client, %key, %state) {
	%term = %client.ct_term;
	if(isObject(%term)) {
		ct_termInputKey(%term, %client.ct_controllerIdx, %key, %state);
	} else if(%term !$= "") {
		talk("ct_clientInputKey: terminal no longer exists: " @ %term);
		%client.ct_term = "";
	}
}
if(!isFunction(serverCmdLInputKey  )) eval("function serverCmdLInputKey  (){}");
if(!isFunction(serverCmdLInputAxis )) eval("function serverCmdLInputAxis (){}");
if(!isFunction(serverCmdLInputStart)) eval("function serverCmdLInputStart(){}");
if(!isFunction(serverCmdLInputStop )) eval("function serverCmdLInputStop (){}");
package ct_customTerminal {
	function serverCmdLInputKey(%client, %key, %state) {
		ct_clientInputKey(%client, %key, %state);
		parent::serverCmdLInputKey(%client, %key, %state);
	}
	function serverCmdLInputAxis(%client, %axis, %amt) {
		ct_clientInputKey(%client, %axis, %amt);
		parent::serverCmdLInputAxis(%client, %axis, %amt);
	}
	function serverCmdLInputStart(%client) {
		parent::serverCmdLInputStart(%client);
	}
	function serverCmdLInputStop(%client) {
		ct_clearClientController(%client);
		parent::serverCmdLInputStop(%client);
	}
};

//---------------------------------
// Client Main Control Interface

function ct_clientClearMainControl(%client, %name, %noUpdate) {
	%term = %client.ct_controlTerm[%name];
	if(isObject(%term)) %term.ct_mainController = "";
	%client.ct_controlTerm[%name] = "";
	for(%i=0; %i<%client.ct_numControlNames; %i++) {
		%name2 = %client.ct_controlName[%i];
		if(%name2 $= %name) {
			%client.ct_controlName[%i] = %client.ct_controlName[%client.ct_numControlNames-1];
			%client.ct_controlName[%client.ct_numControlNames-1] = "";
			%client.ct_numControlNames--;
		}
	}
	ct_clientTermControlStop(%name, %client);
	if(isObject(%term) && !%noUpdate) ct_updateControllerList(%term);
}
function ct_clearMainControl(%term) {
	%client = %term.ct_mainController;
	%name = %term.ct_mainControlName;
	if(%name !$= "") {
		if(isObject(%client)) ct_clientClearMainControl(%client, %name, %client.controlTerm[%name] == %term);
	} else {
		if(isObject(%client)) { talk("ct_clearMainControl - controller exists but no name is set"); return; }
	}
	%term.ct_mainController = "";
	%term.ct_mainControlName = "";
	if(isObject(%client)) ct_clientTermControlStop(%name, %client);
	
	ct_updateControllerList(%term);
}
function ct_setMainControl(%term, %name, %client) {
	if(!isObject(%term)) { talk("ct_setMainControl - no term"); return; }
	if(!isObject(%client)) { talk("ct_setMainControl - no client"); return; }
	if(%name $= "") { talk("ct_setMainControl - no name"); return; }
	
	ct_clearMainControl(%term);
	ct_clientClearMainControl(%client, %name);
	
	%term.ct_mainController = %client;
	%term.ct_mainControlName = %name;
	%client.ct_controlTerm[%name] = %term;
	%client.ct_controlName[%client.ct_numControlNames+0] = %name;
	%client.ct_numControlNames++;
	
	ct_updateControllerList(%term);
	
	ct_clientTermControlStart(%name, %client);
}
function ct_clientTermControlStart(%name, %client) {
	if(!%client.ct_activeCtrl[%name]) {
		%client.ct_activeCtrl[%name] = 1;
		//talk("StartCtrl " @ %name SPC %client.name);
		%term = %client.ct_controlTerm[%name];
		if(!isObject(%term)) { talk("ct_clientTermControlStart - term does not exist"); return; }
		commandToClient(%client, 'ct_startControl', %name, %term.ct_sx, %term.ct_sy);
	}
}
function ct_clientTermControlStop(%name, %client) {
	if(%client.ct_activeCtrl[%name]) {
		%client.ct_activeCtrl[%name] = 0;
		//talk("StopCtrl " @ %name SPC %client.name);
		commandToClient(%client, 'ct_stopControl', %name);
	}
}

$ct_hexDigit[0] = 0; $ct_hexDigit[1] = 1; $ct_hexDigit[2] = 2; $ct_hexDigit[3] = 3; $ct_hexDigit[4] = 4; $ct_hexDigit[5] = 5; $ct_hexDigit[6] = 6; $ct_hexDigit[7] = 7; $ct_hexDigit[8] = 8; $ct_hexDigit[9] = 9; $ct_hexDigit[A] = 10; $ct_hexDigit[B] = 11; $ct_hexDigit[C] = 12; $ct_hexDigit[D] = 13; $ct_hexDigit[E] = 14; $ct_hexDigit[F] = 15;
function ct_fromHex2(%s) {
	%a = $ct_hexDigit[getSubStr(%s, 0, 1)]; if(%a $= "") return "";
	%b = $ct_hexDigit[getSubStr(%s, 1, 1)]; if(%b $= "") return "";
	return %a*16 + %b;
}
function serverCmdCt_updateScreen(%client, %name, %data) {
	%term = %client.ct_controlTerm[%name];
	if(%term $= "") return;
	if(!isObject(%term)) { talk("serverCmdCt_updateScreen - term no longer exists"); %client.ct_controlTerm[%name] = ""; return; }
	if(%term.ct_mainController != %client) { talk("serverCmdCt_updateScreen - client is not term's main controller"); return; }
	if(%term.ct_mainControlName != %name) { talk("serverCmdCt_updateScreen - name is not term's ctrl name"); return; }
	
	%len = strLen(%data);
	%i = 0;
	while(%i < %len) {
		if(%i > %len-6) { talk("serverCmdCt_updateScreen - x/y/len extends past data"); return; }
		%x = ct_fromHex2(getSubStr(%data, %i, 2)); %i+=2;
		%y = ct_fromHex2(getSubStr(%data, %i, 2)); %i+=2;
		%l = ct_fromHex2(getSubStr(%data, %i, 2)); %i+=2;
		if(%x $= "" || %y $= "" || %l $= "") { talk("serverCmdCt_updateScreen - invalid x/y/len: \"" @ getSubStr(%data, %i-6, 6)); return; }
		if(%l+%i > %len) { talk("serverCmdCt_updateScreen - length extends past data"); return; }
		%s = getSubStr(%data, %i, %l); %i+=%l;
		ct_printChars(%term, %x, %y, %s);
	}
	if(%i > %len) { talk("serverCmdCt_updateScreen - length extended past data"); return; }
}
function serverCmdCt_clientInfo(%cl, %target) {
	if(!%cl.isAdmin) return;
	%client = (%target $= "") ? %cl : findClientByName(%target);
	if(!isObject(%client)) return;
	talk("CT Client info for " @ %client.name @ ":");
	talk("  ct_active = " @ %client.ct_active);
	talk("  ct_term = " @ %client.ct_term);
	talk("  ct_controllerIdx = " @ %client.ct_controllerIdx);
	talk("  ct_numControlNames = " @ %client.ct_numControlNames);
	for(%i=0; %i<%client.ct_numControlNames; %i++) {
		%name = %client.ct_controlName[%i];
		talk("  ct_controlName[" @ %i @ "] = " @ %name);
		talk("    ct_controlTerm[" @ %name @ "] = " @ %client.ct_controlTerm[%name]);
		talk("    ct_activeCtrl[" @ %name @ "] = " @ %client.ct_activeCtrl[%name]);
	}
}
function serverCmdCt_termInfo(%cl, %term) {
	if(!%cl.isAdmin) return;
	if(!isObject(%term)) return;
	talk("CT Term info for " @ %term);
	talk("  ct_brickgroup = " @ (isObject(%term.ct_brickgroup) ? %term.ct_brickgroup.getName() : (%term.ct_brickgroup SPC "(DNE)")));
	talk("  ct_nx, ct_ny = " @ %term.ct_nx SPC %term.ct_ny);
	talk("  ct_sx, ct_sy = " @ %term.ct_sx SPC %term.ct_sy);
	talk("  ct_pos, ct_angle = " @ %term.ct_pos @ ", " @ %term.ct_angle);
	%numBricks = 0;
	for(%y=0; %y<%term.ct_sy; %y++) {
		for(%x=0; %x<%term.ct_sx; %x++) {
			%brick = %term.ct_brick[%x, %y];
			if(isObject(%brick)) %numBricks++;
		}
	}
	talk("  ct_brick[x, y] that exist: " @ %numBricks @ "/" @ (%term.ct_sx * %term.ct_sy));
	for(%i=0; %i<$ct_maxControllers; %i++) {
		%client = %term.ct_controller[%i];
		talk("  ct_controller[" @ %i @ "] = " @ %client @ "(" @ (isObject(%client) ? %client.name: "DNE") @ ")");
	}
	%client = %term.ct_mainController;
	talk("  ct_mainController = " @ %client @ "(" @ (isObject(%client) ? %client.name: "DNE") @ ")");
	talk("  ct_mainControlName = " @ %term.ct_mainControlName);
}

activatePackage(ct_customTerminal);

//---------------------------------
// Events

function fxDtsBrick::CustomTerminalCreate(%brick, %resIdx, %sizeX, %sizeY) {
	%resX[0] = 1; %resY[0] = 0.25;
	%resX[1] = 4; %resY[1] = 1;
	%resX[2] = 16; %resY[2] = 4;
	%brick.ct_delete();
	cancel(%brick.ct_createSched);
	%brick.ct_createSched = %brick.schedule(33, ct_create, %resX[%resIdx], %resY[%resIdx], %sizeX, %sizeY);
}
registerOutputEvent("fxDtsBrick", "CustomTerminalCreate",
	"list 1 0 1/4 1 1/16 2" TAB // Resolution
	"int 1 256 64" TAB // X
	"int 1 256 64"
);
function fxDtsBrick::CustomTerminalDelete(%brick) {
	%brick.ct_delete();
}
registerOutputEvent("fxDtsBrick", "CustomTerminalDelete");

function fxDtsBrick::CustomTerminalAssign(%brick, %idx, %mouse, %client) {
	if(!isObject(%client)) return;
	if(!isObject(%brick.ct_term)) return;
	ct_setController(%brick.ct_term, %idx, %client, %mouse);
}
registerOutputEvent("fxDtsBrick", "CustomTerminalAssign", "list 0 0 1 1 2 2 3 3" TAB "bool", 1);

function fxDtsBrick::CustomTerminalUnassign(%brick, %idx) {
	if(!isObject(%brick.ct_term)) return;
	ct_clearController(%brick.ct_term, %idx);
}
registerOutputEvent("fxDtsBrick", "CustomTerminalUnassign", "list 0 0 1 1 2 2 3 3");

function GameConnection::CustomTerminalUnassign(%client) {
	ct_clearClientController(%client);
}
registerOutputEvent("GameConnection", "CustomTerminalUnassign");
function GameConnection::CustomTerminalClearControl(%client, %name) {
	if(%name $= "") return;
	ct_clientClearMainControl(%client, %name);
}
registerOutputEvent("GameConnection", "CustomTerminalClearControl", "string 200 156");

function fxDtsBrick::CustomTerminalSetControl(%brick, %name, %client) {
	if(!isObject(%client)) return;
	if(!isObject(%brick.ct_term)) return;
	if(%name $= "") return;
	ct_setMainControl(%brick.ct_term, %name, %client);
}
registerOutputEvent("fxDtsBrick", "CustomTerminalSetControl", "string 200 156", 1);
function fxDtsBrick::customTerminalClearControl(%brick) {
	if(!isObject(%brick.ct_term)) return;
	ct_clearMainControl(%brick.ct_term);
}
registerOutputEvent("fxDtsBrick", "CustomTerminalClearControl");
